package com.hk.core.autoconfigure.exception;

import com.hk.core.authentication.security.SpringSecurityContext;
import com.hk.core.autoconfigure.exception.reactive.JsonResultErrorAttributes;
import com.hk.core.autoconfigure.exception.reactive.JsonResultErrorWebExceptionHandler;
import com.hk.core.autoconfigure.exception.reactive.WebfluxExceptionHandler;
import com.hk.core.autoconfigure.exception.servlet.*;
import com.hk.commons.exceptions.ServiceException;
import jakarta.persistence.EntityNotFoundException;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
import org.springframework.boot.autoconfigure.web.ServerProperties;
import org.springframework.boot.autoconfigure.web.WebProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.boot.web.reactive.error.ErrorAttributes;
import org.springframework.boot.web.reactive.error.ErrorWebExceptionHandler;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.dao.DataAccessException;
import org.springframework.http.codec.ServerCodecConfigurer;
import org.springframework.web.reactive.result.view.ViewResolver;
import org.springframework.web.server.MethodNotAllowedException;
import org.springframework.web.server.ServerWebExchange;
import org.springframework.web.servlet.DispatcherServlet;

/**
 * 异常处理器配置
 *
 * @author kevin
 * @date 2018-09-10 14:38
 */
@AutoConfiguration
public class ExceptionHandlerConfiguration {

    @Bean
    @ConditionalOnMissingBean(value = {UncaughtExceptionConsumer.class})
    public UncaughtExceptionConsumer exceptionConsumer() {
        return new LoggerUncaughtExceptionConsumer();
    }

    /**
     * servlet 环境
     */
    @Configuration
    @ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET)
    static class ServletExceptionHandlerConfiguration {

        /**
         * Webmvc 异常处理
         */
        @Bean
        @ConditionalOnClass(value = {DispatcherServlet.class})
        public WebmvcExceptionHandler webmvcExceptionHandler() {
            return new WebmvcExceptionHandler();
        }

        /**
         * 数据库访问异常处理
         */
        @Bean
        @ConditionalOnClass(value = {DataAccessException.class})
        public DataExceptionHandler dataExceptionHandler() {
            return new DataExceptionHandler();
        }

        /**
         * Service Exception
         */
        @Bean
        @ConditionalOnClass(value = {ServiceException.class})
        public ServiceExceptionHandler serviceExceptionHandler() {
            return new ServiceExceptionHandler();
        }

        /**
         * Jpa　Exception
         */
        @Bean
        @ConditionalOnClass(value = {EntityNotFoundException.class})
        public JpaExceptionHandler jpaExceptionHandler() {
            return new JpaExceptionHandler();
        }

        /**
         * Spring Security Exception
         */
        @Bean
        @ConditionalOnClass(value = {SpringSecurityContext.class})
        public SpringSecurityExceptionHandler springSecurityExceptionHandler() {
            return new SpringSecurityExceptionHandler();
        }

        /**
         * 公用异常处理
         */
        @Bean
        public SimpleExceptionHandler simpleExceptionHandler() {
            return new SimpleExceptionHandler();
        }
    }

    /**
     * REACTIVE 环境
     */
    @Configuration(proxyBeanMethods = false)
    @ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.REACTIVE)
    @EnableConfigurationProperties(value = {ServerProperties.class, WebProperties.class})
    public static class ReactiveExceptionHandlerConfiguration {

        //  使用Spring 相关接口处理 全局异常
        @Bean
        public ErrorAttributes errorAttributes() {
            return new JsonResultErrorAttributes();
        }

        /**
         * 要注入这个 bean ，不然在进入 Controller 方法之前就抛出异常，如 (请求方法不支持等异常) 时，
         * 还是会走  {@link  ErrorWebExceptionHandler#handle(ServerWebExchange, Throwable)} 方法进行处理，
         * 而使用注解方式配置的会不生效: 如 {@link  WebfluxExceptionHandler#responseStatusException(MethodNotAllowedException, ServerWebExchange)}
         */
        @Bean
        @Order(value = Ordered.HIGHEST_PRECEDENCE)
        public ErrorWebExceptionHandler webExceptionHandler(ServerProperties serverProperties, ErrorAttributes errorAttributes,
                                                            WebProperties webProperties, ObjectProvider<ViewResolver> viewResolvers,
                                                            ServerCodecConfigurer serverCodecConfigurer, ApplicationContext applicationContext) {
            var exceptionHandler = new JsonResultErrorWebExceptionHandler(errorAttributes,
                    webProperties.getResources(),
                    serverProperties.getError(), applicationContext);
            exceptionHandler.setViewResolvers(viewResolvers.orderedStream().toList());
            exceptionHandler.setMessageWriters(serverCodecConfigurer.getWriters());
            exceptionHandler.setMessageReaders(serverCodecConfigurer.getReaders());
            return exceptionHandler;
        }


//      使用Spring 注解处理 全局异常

        /**
         * Webmvc 异常处理
         */
        @Bean
        public WebfluxExceptionHandler webmvcExceptionHandler() {
            return new WebfluxExceptionHandler();
        }

        /**
         * 数据库访问异常处理
         */
        @Bean
        @ConditionalOnClass(value = {DataAccessException.class})
        public com.hk.core.autoconfigure.exception.reactive.DataExceptionHandler dataExceptionHandler() {
            return new com.hk.core.autoconfigure.exception.reactive.DataExceptionHandler();
        }

        /**
         * Service Exception
         */
        @Bean
        @ConditionalOnClass(value = {ServiceException.class})
        public com.hk.core.autoconfigure.exception.reactive.ServiceExceptionHandler serviceExceptionHandler() {
            return new com.hk.core.autoconfigure.exception.reactive.ServiceExceptionHandler();
        }

        /**
         * Spring Security Exception
         */
        @Bean
        @ConditionalOnClass(value = {SpringSecurityContext.class})
        public com.hk.core.autoconfigure.exception.reactive.SpringSecurityExceptionHandler springSecurityExceptionHandler() {
            return new com.hk.core.autoconfigure.exception.reactive.SpringSecurityExceptionHandler();
        }

        /**
         * 公用异常处理
         */
        @Bean
        public com.hk.core.autoconfigure.exception.reactive.SimpleExceptionHandler simpleExceptionHandler() {
            return new com.hk.core.autoconfigure.exception.reactive.SimpleExceptionHandler();
        }

    }


}
